/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package tools;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class offers some static access methods to help with string manipulation and testing
 * 
 * 
 */
public final class StringTools {
   /**
    * Capitalizes the first letters in each of the words in the supplied String
    * 
    * @param string
    * @return
    */
   public static String capitalizeFirstLetters(final String string) {
	  final StringBuilder ff = new StringBuilder();
	  for (final String f : string.split(" ")) {
		 if (ff.length() > 0) {
			ff.append(" ");
		 }
		 ff.append(f.substring(0, 1).toUpperCase()).append(f.substring(1, f.length()).toLowerCase());
	  }
	  return ff.toString();
   }

   /**
    * Returns the amount of occurances of the specific String inside the full string
    * 
    * @param string
    * @param tocount
    * @return
    */
   public static int countOccurance(final String string, final String tocount) {
	  int out = 0;
	  String temp = string;
	  while (temp.matches(".*" + tocount + ".*")) {
		 temp = temp.replaceFirst(tocount, "");
		 ++out;
	  }
	  return out;
   }

   /**
    * Extracts a partial string matching the supplied regex from the complete message
    * 
    * @param partial
    * @param complete
    * @return
    */
   public static String extractPartialString(final String partial, final String complete) {
	  Matcher matcher = Pattern.compile(".*?(" + partial + ").*").matcher(complete);
	  if (matcher.matches())
		 return matcher.group(1);
	  return "";
   }

   /**
    * Returns the levenshtein distance between two strings, i.e. how much it takes to change one string into the other. Downloaded
    * from http://www.merriampark.com/ldjava.htm Thanks to the creator!
    * 
    * @param s
    * @param t
    * @return an integer representing the Levenshtein distance
    */
   public static int getLevenshteinDistance(final String s, final String t) {
	  if (s == null || t == null)
		 throw new IllegalArgumentException("Strings must not be null");

	  /*
	   * The difference between this impl. and the previous is that, rather than creating and retaining a matrix of size
	   * s.length()+1 by t.length()+1, we maintain two single-dimensional arrays of length s.length()+1. The first, d, is the
	   * 'current working' distance array that maintains the newest distance cost counts as we iterate through the characters of
	   * String s. Each time we increment the index of String t we are comparing, d is copied to p, the second int[]. Doing so
	   * allows us to retain the previous cost counts as required by the algorithm (taking the minimum of the cost count to the
	   * left, up one, and diagonally up and to the left of the current cost count being calculated). (Note that the arrays aren't
	   * really copied anymore, just switched...this is clearly much better than cloning an array or doing a System.arraycopy()
	   * each time through the outer loop.) Effectively, the difference between the two implementations is this one does not cause
	   * an out of memory condition when calculating the LD over two very large strings.
	   */

	  final int n = s.length(); // length of s
	  final int m = t.length(); // length of t

	  if (n == 0)
		 return m;
	  else if (m == 0)
		 return n;

	  int p[] = new int[n + 1]; // 'previous' cost array, horizontally
	  int d[] = new int[n + 1]; // cost array, horizontally
	  int _d[]; // placeholder to assist in swapping p and d

	  // indexes into strings s and t
	  int i; // iterates through s
	  int j; // iterates through t

	  char t_j; // jth character of t

	  int cost; // cost

	  for (i = 0; i <= n; i++) {
		 p[i] = i;
	  }

	  for (j = 1; j <= m; j++) {
		 t_j = t.charAt(j - 1);
		 d[0] = j;

		 for (i = 1; i <= n; i++) {
			cost = s.charAt(i - 1) == t_j ? 0 : 1;
			// minimum of cell to the left+1, to the top+1, diagonally left
			// and up +cost
			d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
		 }

		 // copy current distance counts to 'previous row' distance counts
		 _d = p;
		 p = d;
		 d = _d;
	  }

	  // our last action in the above loop was to switch d and p, so p now
	  // actually has the most recent cost counts
	  return p[n];
   }

   /**
    * Checks whether the supplied String is either null or empty
    * 
    * @param in
    * @return
    */
   public static boolean isNullOrEmpty(final String in) {
	  return in == null || "".equals(in);
   }

   /**
    * Validates the supplied input against the supplied InputType
    * 
    * @param type
    * @param input
    * @return
    */
   public static boolean validateInput(final InputType type, final String input) {
	  switch (type) {
	  case AMOUNT:
		 return input.matches("[0-9,]{1,15}[.]{0,1}[0-9]{0,5}[k]{0,4}");
	  case GENERAL:
		 return input.matches("[1-4]{1}");
	  case KDLOC:
		 return input.matches("\\([0-9]{1,2}(:)[0-9]{1,2}\\)");
	  case UTODATE:
		 String month = "(January|February|March|April|May|June|July)";
		 String space = "[ ]";
		 String date = "(?:(?:[0-1][0-9])|(?:[2][0-4])|(?:[1-9]))";
		 String year = "[0-9]{1,2}";
		 return input.matches("(?i)" + month + space + date + "(th, YR|rd, YR|st, YR|nd, YR)" + year)
			   || input.matches("(?i)" + month + space + date + " of YR" + year)
			   || input.matches("(?i)" + month + space + date + space + year);
	  case NICKNAME:
		 return input.matches("[A-Za-z0-9\\{\\}\\|`\\^\\]\\[_\\.]{1,30}");
	  case INT:
		 try {
			Integer.parseInt(input);
			return true;
		 } catch (final NumberFormatException e) {
			return false;
		 }
	  case DOUBLE:
		 try {
			Double.parseDouble(input);
			return true;
		 } catch (final NumberFormatException e) {
			return false;
		 }
	  }
	  return false;
   }

   private StringTools() {
	  // don't allow instantiation
   }
}
